﻿using UnityEngine;
using UnityEngine.UIElements;
using winS.Unity;
using winS.Unity.Geometry;
using static winS.UnityEditor.ProcessEditor.ProcessEditorData;
using Rect = UnityEngine.Rect;

namespace winS.UnityEditor.ProcessEditor
{
    public class Connectionline : GraphElement
    {
        public static readonly float defaultHoverLineWidth = 4f;
        public static readonly float defaultNormalLineWidth = 2f;
        public static readonly float defaultHighlightLineWidth = 5f;
        public static readonly float defaultResponseLineWidth = 12f;

        public override bool movable => false;
        public override bool copyable => false;
        public override bool muteable => false;
        public override bool selectable => false;

        public Color normalColor { get => _normalColor; set => SetNormalColor(value); }
        public Color highlightColor { get => _highlightColor; set => SetHighlightColor(value); }

        public InputPort inputPort { get; protected set; }
        public OutputPort outputPort { get; protected set; }

        protected Vector2 endPoint;
        protected Vector2 startPoint;

        private bool highlighting;

        private float currentLineWidth = defaultNormalLineWidth;

        private Color _normalColor = ConnectionLineSetting.defaultNormalColor;
        private Color _highlightColor = ConnectionLineSetting.defaultHighlightColor;

        private Vector2 lineLeftTopPoint;
        private Vector2 lineRightTopPoint;
        private Vector2 lineLeftBottomPoint;
        private Vector2 lineRightBottomPoint;

        private Vector2 responseLineLeftTopPoint;
        private Vector2 responseLineRightTopPoint;
        private Vector2 responseLineLeftBottomPoint;
        private Vector2 responseLineRightBottomPoint;

        private ViewRect responseLineRect;

        public Connectionline()
        {
            style.color = normalColor;
            style.position = Position.Absolute;
            generateVisualContent += OnGenerateVisualContent;
            RegisterCallback<MouseEnterEvent>(MouseEnterCallback);
            RegisterCallback<MouseLeaveEvent>(MouseLeaveCallback);
        }

        public void EnterNormaltState()
        {
            if (!highlighting) return;
            highlighting = false;
            style.color = normalColor;
            currentLineWidth = defaultNormalLineWidth;
            Update();
        }
        public void EnterHighlightState()
        {
            if (highlighting) return;
            highlighting = true;
            style.color = highlightColor;
            currentLineWidth = defaultHighlightLineWidth;
            Update();
        }
        public void Disconnect()
        {
            inputPort.Disconnect(this);
            outputPort.Disconnect(this);
            inputPort.node.UnregisterCallback<GeometryChangedEvent>(InputPortGeometryChangedCallback);
            outputPort.node.UnregisterCallback<GeometryChangedEvent>(OutputPortGeometryChangedCallback);
        }
        public void Connect(InputPort inputPort, OutputPort outputPort)
        {
            this.inputPort = inputPort;
            this.outputPort = outputPort;
            inputPort.node.RegisterCallback<GeometryChangedEvent>(InputPortGeometryChangedCallback);
            outputPort.node.RegisterCallback<GeometryChangedEvent>(OutputPortGeometryChangedCallback);
            inputPort.Connect(this);
            outputPort.Connect(this);
            startPoint = stepGraphView.connectionlineContainer.WorldToLocal(outputPort.GetConnectorWorldPoint());
            endPoint = stepGraphView.connectionlineContainer.WorldToLocal(inputPort.GetConnectorWorldPoint());
            Update();
        }
        public override bool ContainsPoint(Vector2 localPoint)
        {
            return responseLineRect.Contains(localPoint);
        }
        /// <summary>
        /// 很多情况都会更新线路
        /// </summary>
        protected void Update()
        {
            UpdateLine();
            UpdateRect();
            MarkDirtyRepaint();
        }

        private void UpdateLine()
        {
            Vector2 direction = (endPoint - startPoint).normalized;
            VectorUtility.VerticalDirection(direction, out Vector2 leftDirection, out Vector2 rightDirection, CoordinateType.View);
            float halfLineWidth = currentLineWidth * 0.5f;
            lineLeftTopPoint = startPoint + leftDirection * halfLineWidth;
            lineRightTopPoint = endPoint + leftDirection * halfLineWidth;
            lineLeftBottomPoint = startPoint + rightDirection * halfLineWidth;
            lineRightBottomPoint = endPoint + rightDirection * halfLineWidth;

            float halfResponseLineWidth = defaultResponseLineWidth * 0.5f;
            responseLineLeftTopPoint = startPoint + leftDirection * halfResponseLineWidth;
            responseLineRightTopPoint = endPoint + leftDirection * halfResponseLineWidth;
            responseLineLeftBottomPoint = startPoint + rightDirection * halfResponseLineWidth;
            responseLineRightBottomPoint = endPoint + rightDirection * halfResponseLineWidth;
        }
        private void UpdateRect()
        {
            Math.MinMax(responseLineLeftTopPoint.x, responseLineRightTopPoint.x, responseLineLeftBottomPoint.x, responseLineRightBottomPoint.x, out float minX, out float maxX);
            Math.MinMax(responseLineLeftTopPoint.y, responseLineRightTopPoint.y, responseLineLeftBottomPoint.y, responseLineRightBottomPoint.y, out float minY, out float maxY);

            Rect rect = Rect.MinMaxRect(minX, minY, maxX, maxY);

            style.width = rect.width;
            style.height = rect.height;
            style.top = rect.yMin;
            style.left = rect.xMin;
        }
        private void SetNormalColor(Color newNormalColor)
        {
            _normalColor = newNormalColor;
            if (highlighting) return;
            style.color = newNormalColor;

        }
        private void SetHighlightColor(Color newHighlightColor)
        {
            _highlightColor = newHighlightColor;
            if (!highlighting) return;
            style.color = newHighlightColor;
        }

        private void MouseLeaveCallback(MouseLeaveEvent mouseLeaveEvent)
        {
            if (highlighting)
            {
                style.color = highlightColor;
                currentLineWidth = defaultHighlightLineWidth;
            }
            else currentLineWidth = defaultNormalLineWidth;
            Update();
        }
        private void MouseEnterCallback(MouseEnterEvent mouseEnterEvent)
        {
            if (highlighting)
            {
                style.color = normalColor;
                currentLineWidth = defaultHighlightLineWidth;
            }
            else currentLineWidth = defaultHoverLineWidth;
            Update();
        }
        private void OnGenerateVisualContent(MeshGenerationContext meshGenerationContext)
        {
            Vector2 leftTopPoint = stepGraphView.connectionlineContainer.ChangeCoordinatesTo(this, lineLeftTopPoint);
            Vector2 rightTopPoint = stepGraphView.connectionlineContainer.ChangeCoordinatesTo(this, lineRightTopPoint);
            Vector2 leftBottomPoint = stepGraphView.connectionlineContainer.ChangeCoordinatesTo(this, lineLeftBottomPoint);
            Vector2 rightBottomPoint = stepGraphView.connectionlineContainer.ChangeCoordinatesTo(this, lineRightBottomPoint);

            MeshWriteData meshWriteData = meshGenerationContext.Allocate(4, 6);
            Color color = style.color.value;
            meshWriteData.SetNextVertex(new Vertex() { position = new Vector3(leftTopPoint.x, leftTopPoint.y, Vertex.nearZ), tint = color });
            meshWriteData.SetNextVertex(new Vertex() { position = new Vector3(rightTopPoint.x, rightTopPoint.y, Vertex.nearZ), tint = color });
            meshWriteData.SetNextVertex(new Vertex() { position = new Vector3(rightBottomPoint.x, rightBottomPoint.y, Vertex.nearZ), tint = color });
            meshWriteData.SetNextVertex(new Vertex() { position = new Vector3(leftBottomPoint.x, leftBottomPoint.y, Vertex.nearZ), tint = color });
            meshWriteData.SetNextIndex(0);
            meshWriteData.SetNextIndex(1);
            meshWriteData.SetNextIndex(2);
            meshWriteData.SetNextIndex(0);
            meshWriteData.SetNextIndex(2);
            meshWriteData.SetNextIndex(3);

            Vector2 responseLeftTopPoint = stepGraphView.connectionlineContainer.ChangeCoordinatesTo(this, responseLineLeftTopPoint);
            Vector2 responseRightTopPoint = stepGraphView.connectionlineContainer.ChangeCoordinatesTo(this, responseLineRightTopPoint);
            Vector2 responseLeftBottomPoint = stepGraphView.connectionlineContainer.ChangeCoordinatesTo(this, responseLineLeftBottomPoint);
            Vector2 vector = responseRightTopPoint - responseLeftTopPoint;
            responseLineRect = ViewRect.CreateByTopLeftPoint(responseLeftTopPoint, new Vector2(vector.magnitude, (responseLeftBottomPoint - responseLeftTopPoint).magnitude), vector.normalized);
        }
        private void InputPortGeometryChangedCallback(GeometryChangedEvent geometryChangedEvent)
        {
            startPoint = stepGraphView.connectionlineContainer.WorldToLocal(outputPort.GetConnectorWorldPoint());
            endPoint = stepGraphView.connectionlineContainer.WorldToLocal(inputPort.GetConnectorWorldPoint());
            Update();
        }
        private void OutputPortGeometryChangedCallback(GeometryChangedEvent geometryChangedEvent)
        {
            startPoint = stepGraphView.connectionlineContainer.WorldToLocal(outputPort.GetConnectorWorldPoint());
            endPoint = stepGraphView.connectionlineContainer.WorldToLocal(inputPort.GetConnectorWorldPoint());
            Update();
        }

        public static implicit operator bool(Connectionline self)
        {
            return self != null;
        }
    }
}